package com.ocom.nettyxf.handler;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.HexUtil;
import com.ocom.common.utils.CommonUtil;
import com.ocom.common.utils.NumConvertUtil;
import com.ocom.nettyxf.ropo.TcpServiceRopo;
import com.ocom.nettyxf.service.MachTcpService;
import com.ocom.nettyxf.service.PayService;
import com.ocom.nettyxf.service.XfService;
import com.ocom.nettyxf.util.CRC161;
import com.ocom.nettyxf.util.DES;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static io.netty.handler.codec.stomp.StompHeaders.HEART_BEAT;

/**
 * 数据接受Handler
 */
@Component
@Slf4j
@ChannelHandler.Sharable
public class DataAcceptHandler extends ChannelInboundHandlerAdapter {


//    @Resource
//    RedisUtil redisUtil;


    public static Map<String, ChannelHandlerContext> ctxMap = new ConcurrentHashMap<String, ChannelHandlerContext>(64);
    public static Map<String, String> deviceIps = new ConcurrentHashMap<String, String>(64);


    //签到
    @Autowired
    private MachTcpService machTcpService;

    @Autowired
    private XfService xfService;

    @Autowired
    private PayService payService;


    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.fireChannelActive();
        String msIp = ctx.channel().remoteAddress().toString().substring(1);
        if(!deviceIps.containsKey(msIp)){
            deviceIps.put(msIp,"");
        }
        log.info(" 6UCl580z tcp链接接入：【{}】 ，当前链接数 : 【{}】 ", msIp ,deviceIps.size());
        ctx.writeAndFlush("Your channel is build " +"\r\n");
    }

    // 存放 IP 以及每个IP 对应的断包
    private Map<String, String> histiry = new HashMap<>();

    // 包头
    public static final String START = "caac";
    public static final String END = "acca";

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

//        System.out.println(ctx);

        String messageData=objectToHexStr(msg);
//        String messageData=(String) msg;

        //tcp接入地址端口
        String msIp = ctx.channel().remoteAddress().toString().substring(1);

        //非正常开头和结尾的特殊处理
        if( !(messageData.startsWith(START) &&
                messageData.endsWith(END) )
        ){
            //缓存中数据
            String sdata = histiry.get(msIp) == null ? "" : histiry.get(msIp);
            if (messageData.startsWith(START) && messageData.contains("accacaac") ){
                log.info("Client,处理方式4 -【处理粘包】 【合并删除缓存】 【{}】【{}】" ,msIp, messageData);
                Integer index = messageData.indexOf("accacaac");
                // 替换缓存
                histiry.put(msIp, messageData.substring(index+4));
                messageData = messageData.substring(0,index+4);

            }else if (messageData.endsWith(END) && sdata.startsWith(START) ){
                messageData = sdata + messageData;
                log.info("Client,处理方式2 -【处理拆包】 【合并删除缓存】 【{}】【{}】" ,msIp, messageData);
                histiry.remove(msIp);
            }else if (messageData.startsWith(START) ){
                // 替换缓存
                histiry.put(msIp, messageData);
                log.info("Client,处理方式1 -【处理拆包】 【插入缓存】 【{}】【{}】" ,msIp, messageData);
                return;
            }else{
                log.info("Client,处理方式3 -【处理拆包】 【无效删除缓存】 【{}】【{}】" ,msIp, messageData);
                histiry.remove(msIp);
                return;
            }
        }


        if(messageData.contains("accacaac")){
            String[] split = messageData.split("accacaac");
            String lastMsg = split[split.length-1];
            if(lastMsg.length()>28){
                messageData = "caac" + lastMsg;
            }
        }

        log.info("Client,接收到服务端发来的消息:" + messageData);

        byte[] bytes= NumConvertUtil.hexStringToByte(messageData);
        if (bytes.length < 33) {
            log.info("恶意链接 orgin："+ctx.channel().remoteAddress());
            log.error("恶意链接");
//            ctx.close();
   //         return;
        }

        //判断开头
        String stxLocal=NumConvertUtil.byteToHex(bytes[0],bytes[1]);
        if(!stxLocal.equals("caac")){
            log.error("数据异常-开头不对");
//            ctx.close();
            return;
        }

        //判断结尾
        String extLocal=NumConvertUtil.byteToHex(bytes[bytes.length-2],bytes[bytes.length-1]);
        if(!extLocal.equals("acca")){
            log.error("数据异常-结尾不对");
//            ctx.close();
            return;
        }

        //判断长度
        String strLengthLocal=NumConvertUtil.byteToHex(bytes[2],bytes[3]);
        //16转10
        Integer b=Integer.parseInt(strLengthLocal,16);
        if(b+6!=bytes.length){
//            System.out.println(b);
//            System.out.println(bytes.length);
            log.error("数据异常-总长度错误1");
      //      ctx.close();
            return;
        }
        //判断命令长度 参数长度
        String commandLength=NumConvertUtil.byteToHex(bytes[21],bytes[22]);
        Integer length=Integer.parseInt(commandLength,16);

        if(length+33!=bytes.length){
            log.error("数据异常-参数长度错误2");
       //     ctx.close();
            return;
        }

        //校验数据:
        String yanqian=messageData.substring(8,messageData.length()-20);
//        System.out.println("yanqian："+yanqian);
        //crc16
        String crcHexStr= CRC161.getCRC3(NumConvertUtil.hexStringToByte(yanqian));
//        System.out.println(crcHexStr);
        if(crcHexStr.length()==2){
            crcHexStr+="00";
        }
//        if(crcHexStr.length()==3){
//            StringBuilder stringBuilder=new StringBuilder(crcHexStr);
//            stringBuilder.insert(2,"0");
//            crcHexStr=stringBuilder.toString();
//        }
//        System.out.println("crc:"+crcHexStr);
        //异或和
        String yhh=NumConvertUtil.hexYihuo(yanqian);
        if(yhh.length()==1){
            yhh="0"+yhh;
        }
//        System.out.println("yhh:"+yhh);
        //累加和
        String ljh=NumConvertUtil.hexLeijia(yanqian);
        if(ljh.length()==1){
            ljh+="0";
        }
//        System.out.println("ljh:"+ljh);
        //包号
        String packageNum=NumConvertUtil.byteToHex(bytes[11],bytes[12],bytes[13],bytes[14]);

        //验签
        String signData=packageNum+crcHexStr+yhh+ljh;

        //des加密
        DES des = new DES(NumConvertUtil.hexStringToByte("0044C3D1237CA268"));
        byte[] bytes1 = des.encrypt(NumConvertUtil.hexStringToByte(signData));
//        System.out.println("longSign:"+NumConvertUtil.bytesToHexString(bytes1));
        String tcpSign=NumConvertUtil.bytesToHexString(bytes1).substring(0, 16);

        byte[] signBytes=new byte[8];
        for(int i=bytes.length-10;i<bytes.length-2;i++){
            signBytes[i-(bytes.length-10)]=bytes[i];
        }
        //tcp上的sign
        String sign=NumConvertUtil.byteToHex(signBytes);

        if(!tcpSign.equals(sign)){
//            System.out.println(sign);
//            System.out.println(tcpSign);
            log.error("验签失败");
//            ctx.close();
            return;
        }

        TcpServiceRopo tcpServiceRopo=new TcpServiceRopo(bytes);
//        System.out.println(tcpServiceRopo);
//        logger.info("收到TCP信息 tcpServiceRopo：{}" ,tcpServiceRopo);

        //记录tcp 长链接
        ctxMap.put(tcpServiceRopo.getMachId(),ctx);
        deviceIps.put(msIp,tcpServiceRopo.getMachId());

        log.info(" aN58OsiU 设备 【{}】ip 【{}】链接数 【{}】  ",tcpServiceRopo.getMachId(),msIp,deviceIps.size());

//        redisUtil.hmSet("tcpXfMach:" + tcpServiceRopo.getMachId(), "ip", IPUtils.getLocalIp());

//        redisUtil.set("Mach#server#"+tcpServiceRopo.getMachId(),server);

        switch (tcpServiceRopo.getCommandType()) {
            //签到(0x0001)
            case "0001" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.sign(tcpServiceRopo)));
                break;
            // 心跳 (0x0003)
            case "0003" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.heartbeat(tcpServiceRopo)));
                break;
            // 参数更新 (0x0005)
            case "0005" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.paramUpdate(tcpServiceRopo)));
                break;
            //  参数更新结果通知 (0x1005)
            case "1005" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.paramUpdateResult(tcpServiceRopo)));
                break;
            //  新消费汇总查询 (0x0006)
            case "0006" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(payService.payCount(tcpServiceRopo)));
                break;
            //  固件更新续传 (0xcc00)
            case "cc00" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.machUpdate(tcpServiceRopo)));
                break;
            //  固件更新结果通知续传（0xdc00）
            case "dc00" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.machUpdateResult(tcpServiceRopo)));
                break;
            //  按订单退款命令 (0x0007)
            case "0007" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(payService.refund(tcpServiceRopo)));
                break;
            //  消费记录上传（离线） (0x0008)
            case "0008" :
                if (isAllF(tcpServiceRopo.getCommandParam())) {
                    log.error("【异常消费记录上传】设备：【{}】订单号：【{}】",tcpServiceRopo.getMachId(),tcpServiceRopo.getCommandParam().substring(4, 30));
                    ctx.close();
                    return;
                }
                byte[] retByte = payService.recodeUpload(tcpServiceRopo);
                if(retByte == null){
                    log.error("【异常消费记录上传】设备：【{}】订单号：【{}】",tcpServiceRopo.getMachId(),tcpServiceRopo.getCommandParam().substring(4, 30));
                    ctx.close();
                    return;
                }
                ctx.writeAndFlush(Unpooled.wrappedBuffer(retByte));
                break;
            //  余额查询（0x0009）
            case "0009" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(payService.balanceQuery(tcpServiceRopo)));
                break;
            //  ocom离线码秘钥更新（0x0010）
            case "0010" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.secretUpdate(tcpServiceRopo)));
                break;
            //  Ocom离线码秘钥更新结果通知（0x1010）
            case "1010" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.secretUpdateResult(tcpServiceRopo)));
                break;
            //  离线风控参数更新 (0x0011)
            case "0011" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.fkParamUpdate(tcpServiceRopo)));
                break;
            //  离线风控参数更新结果(0x1011)
            case "1011" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.fkParamUpdateResult(tcpServiceRopo)));
                break;
            //  白名单全量/增量更新（0x0012）
            case "0012" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.whiteList(tcpServiceRopo)));
                break;
            //  白名单全量/增量更新结果通知（0x1012）
            case "1012" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(xfService.whiteListResult(tcpServiceRopo)));
                break;
            //  支付请求（强在线）（0x0013）
            case "0013" :
                ctx.writeAndFlush(Unpooled.wrappedBuffer(payService.pay(tcpServiceRopo)));
                break;
            //  强在线订单查询（0x0014）
            case "0014" :
                String dateStr =  tcpServiceRopo.getCommandParam().substring(12, 26); // 要判断的日期字符串
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); // 设置日期格式
                try {
                    Date date = sdf.parse(dateStr); // 将字符串转换为Date对象进行校验
                    long minBetween  = DateUtil.between(new Date(), date, DateUnit.MINUTE);
                    if(minBetween>=2l && minBetween<= 259200l){ //2分钟 至 180天
                        log.error("【超长时间查询】设备：【{}】订单号：【{}】",tcpServiceRopo.getMachId(),tcpServiceRopo.getCommandParam().substring(4, 30));
                        ctx.close();
                        return;
                    }
                } catch (ParseException e) {
                    log.error("【超长时间查询】 【异常：时间解析错误】设备：【{}】订单号：【{}】",tcpServiceRopo.getMachId(),tcpServiceRopo.getCommandParam().substring(4, 30));
                }

                byte[] bytes2  = payService.orderQuery(tcpServiceRopo);
                if(bytes2==null )  {
                    return;
                }
                ctx.writeAndFlush(Unpooled.wrappedBuffer(bytes2));
                break;
            default:
                log.error("无效请求参数");
//                ctx.close();
//                logger.info("无效请求参数");
//                ctx.writeAndFlush(Unpooled.wrappedBuffer(machTcpService.sign(tcpServiceRopo)));

        }



    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error(cause.getMessage(), cause);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx){
        removeChannelMap(ctx);
//        log.info(ctx.channel().remoteAddress() + "inactive");
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
//        if (evt instanceof IdleStateEvent) {
//            log.error("timeout");
//            ctx.channel().close();
//        }
        if (evt instanceof IdleStateEvent){
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt ;
            if (idleStateEvent.state() == IdleState.READER_IDLE){
                String msIp = ctx.channel().remoteAddress().toString().substring(1);
                String deviceNo = deviceIps.get(msIp);
                log.info(" hEcyNyu8 已经120秒没有收到信息！设备【{}】ip 【{}】",deviceNo,msIp);
                //向客户端发送消息
                ctx.writeAndFlush(HEART_BEAT).addListener(ChannelFutureListener.CLOSE_ON_FAILURE) ;
            }
        }
        super.userEventTriggered(ctx, evt);
    }


    /**
     * 移除已经失效的链接
     * @param ctx
     */
    private void removeChannelMap(ChannelHandlerContext ctx) {
        String msIp = ctx.channel().remoteAddress().toString().substring(1);
        histiry.remove(msIp);
        if(deviceIps.containsKey(msIp)){
            ctxMap.remove(deviceIps.get(msIp));
        }
        log.info(" 6UCl580z   移除设备： 【{}】  移除ip： 【{}】  当前链接数： 【{}】" , deviceIps.get(msIp),msIp ,deviceIps.size()-1);
        deviceIps.remove(msIp);
    }


    private static String objectToHexStr(Object msg) {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);//复制内容到字节数组bytes
        //内存泄漏 回收对象
        buf.release();
        return HexUtil.encodeHexStr(bytes);//将接收到的数据转为字符串，此字符串就是客户端发送的字符串
    }

    public static boolean sendMessage(String keyid,TcpServiceRopo tcpServiceRopo) throws Exception {
//        for(Map.Entry entry : ctxMap.entrySet()){
//            String mapKey = (String) entry.getKey();
//            ChannelHandlerContext mapValue = (ChannelHandlerContext)entry.getValue();
//            System.out.println(mapKey+":"+mapValue);
//        }

        if(!ctxMap.containsKey(keyid)){
//            System.out.println("链接已经关闭");
            return false;
        }
        ChannelHandlerContext ctx = ctxMap.get(keyid);
        ctx.write(Unpooled.wrappedBuffer(NumConvertUtil.hexStringToByte(tcpServiceRopo.getHexStr())));
        ctx.flush();
        return true;
    }

    public static boolean isAllF(String str) {
        return str.replaceAll("[^f]", "").equals(str);
    }



    public static void main(String[] args){

        //caac0035010121074084010000011b9e473bec0013001a0101012107408420250304115445003d0000032000045157ef2f573cabcde6d8c156accacaac0034010121074084010000011c9e473bec0014001901012107408420250304115445003d0000032000045157ef2f464f20ecb2
        String messageData="caac0035010121074084010000011b9e473bec0013001a0101012107408420250304115445003d0000032000045157ef2f573cabcde6d8c156accacaac0034010121074084010000011c9e473bec0014001901012107408420250304115445003d0000032000045157ef2f464f20ecb2";

        //tcp接入地址端口
        String msIp = "127.0.0.1";
//        String messageData=(String) msg;
        // 存放 IP 以及每个IP 对应的断包
         Map<String, String> histiry = new HashMap<>();

        //非正常开头和结尾的特殊处理
        if( !(messageData.startsWith(START) &&
                messageData.endsWith(END) )
        ){
            //缓存中数据
            String sdata = histiry.get(msIp) == null ? "" : histiry.get(msIp);
            if (messageData.startsWith(START) && messageData.contains("accacaac") ){
                Integer index = messageData.indexOf("accacaac");
                // 替换缓存
                histiry.put(msIp, messageData.substring(index+4));
                messageData = messageData.substring(0,index+4);

            }else if (messageData.endsWith(END) && sdata.startsWith(START) ){
                messageData = sdata + messageData;
                log.info("Client,处理方式2 -【处理拆包】 【合并删除缓存】 【{}】【{}】" ,msIp, messageData);
                histiry.remove(msIp);
            }else if (messageData.startsWith(START) ){
                // 替换缓存
                histiry.put(msIp, messageData);
                log.info("Client,处理方式1 -【处理拆包】 【插入缓存】 【{}】【{}】" ,msIp, messageData);
                return;
            }else{
                log.info("Client,处理方式3 -【处理拆包】 【无效删除缓存】 【{}】【{}】" ,msIp, messageData);
                histiry.remove(msIp);
                return;
            }
        }

        System.out.println(messageData);
        System.out.println(  histiry.get(msIp));

    }
}
